package com.h117121.fashionshop.core.dao;

import java.util.List;

import org.hibernate.Query;
import org.springframework.stereotype.Repository;

import com.h117121.fashionshop.core.dao.generic.GenericDaoImpl;
import com.h117121.fashionshop.core.dom.Product;
import com.h117121.fashionshop.core.dom.ProductQuery;
import com.h117121.fashionshop.core.dom.ResultPage;
import com.h117121.fashionshop.core.exception.EntityNotFoundException;

@Repository("productDao")
public class ProductDaoImpl extends GenericDaoImpl<Product, Long> implements
		ProductDao {
	@Override
	public ResultPage findByKeyword(String keyword, int index, int numItems) {
		ProductQuery query = new ProductQuery();
		query.setProductName(keyword);
		query.setProducerName(keyword);
		query.setProductType(keyword);
		query.setProductMadein(keyword);
		return findByProductQuery(query, AndOrQueryType.OR, index, numItems);
	}

	@Override
	public ResultPage getNewestProducts(int index, int numItems) {
		Query query = getSessionFactory().getCurrentSession().createQuery(
				"from Product order by dateAdded desc");
		query.setMaxResults(numItems);
		query.setFirstResult(index * numItems);

		long totalNumItems = (((Long) getSessionFactory().getCurrentSession()
				.createQuery("select count(*) from Product").iterate().next())
				.longValue());
		long numPage = (totalNumItems % numItems == 0) ? (totalNumItems / numItems)
				: (1 + totalNumItems / numItems);

		ResultPage result = new ResultPage();
		result.setNumPage(numPage);
		result.setCurrentPage(index);
		result.setNumItems(totalNumItems);
		result.setResult(query.list());

		return result;
	}

	@Override
	public ResultPage getMostSoldProducts(int index, int numItems) {
		Query query = getSessionFactory().getCurrentSession().createQuery(
				"from Product order by amountSold desc");
		query.setMaxResults(numItems);
		query.setFirstResult(index * numItems);

		long totalNumItems = (((Long) getSessionFactory().getCurrentSession()
				.createQuery("select count(*) from Product").iterate().next())
				.longValue());
		long numPage = (totalNumItems % numItems == 0) ? (totalNumItems / numItems)
				: (1 + totalNumItems / numItems);

		ResultPage result = new ResultPage();
		result.setNumPage(numPage);
		result.setCurrentPage(index);
		result.setNumItems(totalNumItems);
		result.setResult(query.list());

		return result;
	}

	@Override
	public ResultPage findByProductQuery(ProductQuery query,
			AndOrQueryType andor, int index, int numItems) {
		if (query.getKeyWord() != null) {
			return findByKeyword(query.getKeyWord(), index, numItems);
		} else {
			StringBuilder queryString = new StringBuilder("from Product where ");

			StringBuilder productNameClause = new StringBuilder();
			StringBuilder productTypeClause = new StringBuilder();
			StringBuilder producerNameClause = new StringBuilder();
			StringBuilder productMadeinClause = new StringBuilder();
			StringBuilder priceClause = new StringBuilder();

			boolean flag = false;

			if (query.getProductName() != null
					&& !query.getProductName().isEmpty()) {
				productNameClause.append(" lower(name) like "
						+ formatLikeString(query.getProductName()));
				flag = true;
			}
			if (query.getProductType() != null
					&& !query.getProductType().isEmpty()) {
				if (flag)
					productTypeClause.append(" " + andor + " ");
				productTypeClause.append(" lower(type.name) like "
						+ formatLikeString(query.getProductType()));
				flag = true;
			}
			if (query.getProducerName() != null
					&& !query.getProducerName().isEmpty()) {
				if (flag)
					producerNameClause.append(" " + andor + " ");
				producerNameClause.append(" lower(producer.name) like "
						+ formatLikeString(query.getProducerName()));
				flag = true;
			}
			if (query.getProductMadein() != null
					&& !query.getProductMadein().isEmpty()) {
				if (flag)
					productMadeinClause.append(" " + andor + " ");
				productMadeinClause.append(" lower(madeIn) like "
						+ formatLikeString(query.getProductMadein()));
				flag = true;
			}

			if (query.getPriceFrom() != null) {
				if (flag)
					priceClause.append(" " + andor + " ");
				priceClause.append(" price >= "
						+ query.getPriceFrom().toString() + " ");
				flag = true;
			}

			if (query.getPriceTo() != null) {
				if (flag)
					priceClause.append(" " + andor + " ");
				priceClause
						.append(" price <= " + query.getPriceTo().toString());
				flag = true;
			}

			queryString.append(productNameClause).append(productTypeClause)
					.append(producerNameClause).append(productMadeinClause)
					.append(priceClause);

			Query queryObj = getSessionFactory().getCurrentSession()
					.createQuery(queryString.toString());
			queryObj.setMaxResults(numItems);
			queryObj.setFirstResult(index * numItems);

			long totalNumItems = ((Long) getSessionFactory()
					.getCurrentSession()
					.createQuery(
							(new StringBuilder(
									"select count(*) from Product where ")
									.append(productNameClause)
									.append(productTypeClause)
									.append(producerNameClause)
									.append(productMadeinClause)
									.append(priceClause)).toString()).iterate()
					.next()).intValue();
			long numPage = (totalNumItems % numItems == 0) ? (totalNumItems / numItems)
					: (1 + totalNumItems / numItems);

			ResultPage result = new ResultPage();
			result.setNumPage(numPage);
			result.setCurrentPage(index);
			result.setNumItems(totalNumItems);
			result.setResult(queryObj.list());

			return result;
		}
	}

	@Override
	public ResultPage getSimilarProducts(long id, int index, int numItems) {
		Product product = null;
		try {
			product = findById(id);
		} catch (EntityNotFoundException e) {
			ResultPage rs = new ResultPage();
			return rs;
		}
		ProductQuery query = new ProductQuery();
		query.setProducerName(product.getProducer().getName());
		query.setProductMadein(product.getMadeIn());
		query.setProductType(product.getType().getName());

		StringBuilder queryString = new StringBuilder("from Product p where ");

		StringBuilder productNameClause = new StringBuilder();
		StringBuilder productTypeClause = new StringBuilder();
		StringBuilder producerNameClause = new StringBuilder();
		StringBuilder productMadeinClause = new StringBuilder();
		StringBuilder priceClause = new StringBuilder();

		boolean flag = false;

		if (query.getProductType() != null && !query.getProductType().isEmpty()) {
			if (flag)
				productTypeClause.append(" " + "AND" + " ");
			productTypeClause.append(" lower(type.name) like "
					+ formatLikeString(query.getProductType()));
			flag = true;
		}
		if (query.getProducerName() != null
				&& !query.getProducerName().isEmpty()) {
			if (flag)
				producerNameClause.append(" " + "AND" + " ");
			producerNameClause.append(" lower(producer.name) like "
					+ formatLikeString(query.getProducerName()));
			flag = true;
		}
		if (query.getProductMadein() != null
				&& !query.getProductMadein().isEmpty()) {
			if (flag)
				productMadeinClause.append(" " + "AND" + " ");
			productMadeinClause.append(" lower(madeIn) like "
					+ formatLikeString(query.getProductMadein()));
			flag = true;
		}

		queryString.append(productNameClause).append(productTypeClause)
				.append(producerNameClause).append(productMadeinClause)
				.append(priceClause);
		queryString.append(" order by random()");

		Query queryObj = getSessionFactory().getCurrentSession().createQuery(
				queryString.toString());
		queryObj.setMaxResults(numItems);
		queryObj.setFirstResult(0);

		ResultPage result = new ResultPage();
		result.setNumPage(1);
		result.setCurrentPage(index);
		result.setNumItems(numItems);
		result.setResult(queryObj.list());

		return result;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Product> getRandomProducts(int numItems) {
		StringBuilder queryString = new StringBuilder(
				"from Product order by random()");

		Query queryObj = getSessionFactory().getCurrentSession().createQuery(
				queryString.toString());
		queryObj.setMaxResults(numItems);
		queryObj.setFirstResult(0);
		return queryObj.list();
	}
}